#include "ShadowMapPass.h"
#include "RenderScene.h"
#include <Common.h>
#include <Interface.h>
#include "Utils.h"
#include <glm/ext.hpp>
#include <GLM/gtc/type_ptr.hpp>

CShadowMapPass::CShadowMapPass(const std::string& vPassName, int vExecutionOrder) : IRenderPass(vPassName, vExecutionOrder)
{
}

void CShadowMapPass::initV()
{
	m_pShader = std::make_shared<CShader>("Shaders/ShadowMap.vs", "Shaders/ShadowMap.fs", "", "Shaders/ShadowMap.tcs", "Shaders/ShadowMap.tes");
	//m_pShader = std::make_shared<CShader>("Shaders/ShadowMap.vs", "Shaders/ShadowMap.fs");

	auto FrontTexture = std::make_shared<Graphics::STexture>();
	auto BackTexture = std::make_shared<Graphics::STexture>();
	FrontTexture->InternalFormat = BackTexture->InternalFormat = GL_DEPTH_COMPONENT32F;
	FrontTexture->ExternalFormat = BackTexture->ExternalFormat = GL_DEPTH_COMPONENT;
	FrontTexture->DataType = BackTexture->DataType = GL_FLOAT;
	FrontTexture->Width = FrontTexture->Height = BackTexture->Width = BackTexture->Height = m_ShadowMapResolution;
	FrontTexture->Type4MinFilter = BackTexture->Type4MinFilter = GL_NEAREST;
	FrontTexture->Type4MagFilter = BackTexture->Type4MagFilter = GL_NEAREST;
	FrontTexture->Type4WrapS = FrontTexture->Type4WrapT = FrontTexture->Type4WrapR = GL_CLAMP_TO_EDGE;
	BackTexture->Type4WrapS = BackTexture->Type4WrapT = BackTexture->Type4WrapR = GL_CLAMP_TO_EDGE;
	FrontTexture->TextureAttachmentType = BackTexture->TextureAttachmentType = Graphics::STexture::ETextureAttachmentType::DepthTexture;
	genTexture(FrontTexture);
	genTexture(BackTexture);

	// m_FBO = genFBO({ TextureConfig4Depth }); // bug!
	m_FBOs.resize(2);
	glGenFramebuffers(1, &(GLuint&)m_FBOs[0]);
	glBindFramebuffer(GL_FRAMEBUFFER, m_FBOs[0]);
	glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, FrontTexture->TextureID, 0);
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);
	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
	{
		std::cerr << "Error::FBO:: Framebuffer Is Not Complete." << std::endl;
	}

	glGenFramebuffers(1, &(GLuint&)m_FBOs[1]);
	glBindFramebuffer(GL_FRAMEBUFFER, m_FBOs[1]);
	glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, BackTexture->TextureID, 0);
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);
	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
	{
		std::cerr << "Error::FBO:: Framebuffer Is Not Complete." << std::endl;
	}

	Graphics::ResourceManager::registerSharedData("FrontDepthTexture", FrontTexture);
	Graphics::ResourceManager::registerSharedData("BackDepthTexture", BackTexture);

	m_pShader->activeShader();

	glm::vec3 LightPos(0.0f, 0.0f, 2.0f);
	Graphics::ResourceManager::registerSharedData("LightPos", LightPos);
	m_pShader->setFloatUniformValue("u_LightPos", LightPos.x, LightPos.y, LightPos.z);

	float NearPlane = Graphics::Camera::getMainCameraNear();
	float FarPlane = Graphics::Camera::getMainCameraFar();
	m_pShader->setFloatUniformValue("u_NearPlane", NearPlane);
	m_pShader->setFloatUniformValue("u_FarPlane", FarPlane);
	glm::mat4 LightViewMatrix = glm::lookAt(LightPos, LightPos + glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f));
	m_pShader->setMat4UniformValue("u_LightViewMatrix", glm::value_ptr(LightViewMatrix));
	Graphics::ResourceManager::registerSharedData("LightViewMatrix", LightViewMatrix);
	m_pShader->setFloatUniformValue("u_Direction", 1.0f);
}

void CShadowMapPass::updateV()
{
	glViewport(0, 0, m_ShadowMapResolution, m_ShadowMapResolution);

	for (int i = 0; i < 2; i++)
	{
		glBindFramebuffer(GL_FRAMEBUFFER, m_FBOs[i]);
		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glEnable(GL_DEPTH_TEST);
		glEnable(GL_CULL_FACE);

		m_pShader->activeShader();
		float Direction = (i % 2 == 0 ? 1.0f : -1.0f);
		
		m_pShader->setFloatUniformValue("u_Direction", Direction);
		glPatchParameteri(GL_PATCH_VERTICES, 3);
		renderScene(m_pShader, true);

		glBindFramebuffer(GL_FRAMEBUFFER, 0);
	}

	glViewport(0, 0, Graphics::WINDOW_KEYWORD::getWindowWidth(), Graphics::WINDOW_KEYWORD::getWindowHeight());
}
